package com.letu.aopcache.manager;

import cn.hutool.core.collection.CollectionUtil;
import com.letu.annotation.annotation.cache.Cache;
import com.letu.annotation.annotation.cache.Magic;
import com.letu.aopcache.aop.CacheAopProxyChain;
import com.letu.aopcache.model.*;
import com.letu.aopcache.parser.AbstractScriptParser;
import com.letu.aopcache.util.CacheUtil;
import com.letu.aopcache.util.MSetParam;
import com.letu.core.utils.StringTLongUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 缓存key 管理类
 */
@Slf4j
public class CacheKeyManager {

    private final ICacheManager cacheManager;

    private final AutoLoadConfig config;

    /**
     * 表达式解析器
     */
    private final AbstractScriptParser scriptParser;


    public CacheKeyManager(ICacheManager cacheManager, AbstractScriptParser scriptParser, AutoLoadConfig config) throws IllegalArgumentException {
        if (null == cacheManager) {
            throw new IllegalArgumentException("cacheManager is null");
        }
        if (null == scriptParser) {
            throw new IllegalArgumentException("scriptParser is null");
        }
        this.cacheManager = cacheManager;
        this.config = config;
        this.scriptParser = scriptParser;
    }


    /**
     * 生成缓存KeyTO
     *
     * @param methodMsg        方法基本信息
     * @param keyExpression    key表达式
     * @param hfieldExpression hfield表达式
     * @param result           执行实际方法的返回值
     * @param hasRetVal        是否有返回值
     * @return CacheKeyTO
     */
    public CacheKeyTO getInterfaceCacheKey(MethodMsg methodMsg, String keyExpression,
                                           String hfieldExpression, Object result, boolean hasRetVal) {
        String key = null;
        String hfield = null;
        if (null != keyExpression && keyExpression.trim().length() > 0) {
            try {
                Object[] objects = new Object[]{methodMsg.getArgs(), methodMsg.getHeader()};
                key = scriptParser.getDefinedCacheKey(keyExpression, methodMsg.getTarget(), objects, result, hasRetVal);
                if (null != hfieldExpression && hfieldExpression.trim().length() > 0) {
                    hfield = scriptParser.getDefinedCacheKey(hfieldExpression, methodMsg.getTarget(), objects, result, hasRetVal);
                }
            } catch (Exception ex) {
                log.error(ex.getMessage(), ex);
            }
        } else {
            key = CacheUtil.getDefaultCacheKey(methodMsg.getTarget().getClass().getName(), methodMsg.getMethodName(), methodMsg.getArgs(), methodMsg.getHeader());
        }
        if (null == key || key.trim().isEmpty()) {
            throw new IllegalArgumentException("cache key for " + methodMsg.getTarget().getClass().getName() + "." + methodMsg.getMethodName() + " is empty");
        }
        //自定义key值:  类名:方法名:hash值
        key = methodMsg.getClassName() + ":" + methodMsg.getMethodName() + ":" + key;
        return new CacheKeyTO(config.getNamespace(), key, hfield);
    }


    /**
     * 生成 同样的方法,接口,header 唯一key，共享用
     *
     * @param methodMsg 方法基本信息
     * @param cache     cache
     * @return CacheKeyTO
     */
    public CacheKeyTO getOnlyKey(Cache cache, MethodMsg methodMsg) {
        String keyExpression = cache.key();
        String hfieldExpression = cache.hfield();
        String key = null;
        String hfield = null;
        if (null != keyExpression && keyExpression.trim().length() > 0) {
            try {
                Object[] objects = new Object[]{methodMsg.getArgs(), methodMsg.getHeader()};
                key = scriptParser.getDefinedCacheKey(keyExpression, methodMsg.getTarget(), objects, null, false);
                if (null != hfieldExpression && hfieldExpression.trim().length() > 0) {
                    hfield = scriptParser.getDefinedCacheKey(hfieldExpression, methodMsg.getTarget(), objects, null, false);
                }
            } catch (Exception ex) {
                log.error(ex.getMessage(), ex);
            }
        } else {
            key = CacheUtil.getDefaultCacheKey(methodMsg.getTarget().getClass().getName(), methodMsg.getMethodName(), methodMsg.getArgs(), methodMsg.getHeader());
        }
        if (null == key || key.trim().isEmpty()) {
            throw new IllegalArgumentException("cache key for " + methodMsg.getTarget().getClass().getName() + "." + methodMsg.getMethodName() + " is empty");
        }
//        log.info("唯一 key is {}", key);
        //自定义key值:  类名:方法名:hash值
        //压缩下key
        key = String.valueOf(StringTLongUtil.stringToLongOfMd5(key));
        return new CacheKeyTO(config.getNamespace(), key, hfield);
    }

    /**
     * 接口缓存 生成缓存 Key
     *
     * @param onlyCacheKeyTO
     * @param methodMsg
     * @return String 缓存Key
     */
    public CacheKeyTO getInterfaceCacheKey(CacheKeyTO onlyCacheKeyTO, MethodMsg methodMsg) {
        String key = onlyCacheKeyTO.getKey();
        //自定义key值:  类名:方法名:hash值
        key = methodMsg.getClassName() + ":" + methodMsg.getMethodName() + ":" + key;
        //压缩下key
        key = String.valueOf(StringTLongUtil.stringToLongOfMd5(key));
        return new CacheKeyTO(config.getNamespace(), key, onlyCacheKeyTO.getHfield());
    }
    /**
     * 接口缓存 生成缓存 Key
     *
     * @param pjp
     * @param cache
     * @return String 缓存Key
     */
    public CacheKeyTO getInterfaceCacheKey(CacheAopProxyChain pjp, Cache cache, MethodMsg methodMsg) {
        String keyExpression = cache.key();
        String hfieldExpression = cache.hfield();
        return getInterfaceCacheKey(methodMsg, keyExpression, hfieldExpression, null, false);
    }


    /**
     * 关联关系缓存 生成缓存 Key
     *
     * @param cache
     * @param methodMsg
     * @param cacheWrapper
     * @return
     */
    public List<CacheKeyTO> getRelatedCacheKey(CacheKeyTO onlyCacheKeyTO, Cache cache, MethodMsg methodMsg, CacheWrapper<Object> cacheWrapper) throws Exception {
        if (cacheWrapper == null || cacheWrapper.getIds() == null || cacheWrapper.getIds().isEmpty()) {
            return null;
        }
        List<CacheKeyTO> cacheKeyTOList = new ArrayList<>();
        for (Long id : cacheWrapper.getIds()) {
            String key = cacheWrapper.getObjectClassName() + ":"
                    + CacheWayEnum.HASH.getCode() + ":"
                    + id + ":"
                    + onlyCacheKeyTO.getKey();
            cacheKeyTOList.add(new CacheKeyTO(config.getNamespace(), key, onlyCacheKeyTO.getHfield()));
        }
        return cacheKeyTOList;
    }


    /**
     * 关联关系缓存 生成缓存 Key
     *
     * @param cache
     * @param methodMsg
     * @param cacheWrapper
     * @return
     */
    public List<CacheKeyTO> getRelatedCacheKeyOfMagic(CacheKeyTO onlyCacheKeyTO, Cache cache, MethodMsg methodMsg, CacheWrapper<Object> cacheWrapper, AbstractScriptParser scriptParser) throws Exception {
        if (cacheWrapper == null || cacheWrapper.getIds() == null || cacheWrapper.getIds().isEmpty()) {
            return null;
        }
        Magic magic = cache.magic();
        List<MagicCacheParam> magicCacheParamList = scriptParser.parseMagicParam(magic.key(), methodMsg.getArgs());
        if (magicCacheParamList == null || magicCacheParamList.isEmpty()) {
            //走hash模式
            return getRelatedCacheKey(onlyCacheKeyTO, cache, methodMsg, cacheWrapper);
        }
        List<CacheKeyTO> cacheKeyTOList = new ArrayList<>();
        for (MagicCacheParam magicCacheParam : magicCacheParamList) {
            if (!StringUtils.isNotBlank(magicCacheParam.getWhereType())) {
                magicCacheParam.setWhereType("eq");
            }
            Object paramObject = magicCacheParam.getValues();
            if (paramObject == null) {
                continue;
            }
            String key = null;
            if (paramObject instanceof Collection) {
                List<Object> paramList = (List<Object>) paramObject;
                for (Object object : paramList) {
//                    key = cacheWrapper.getObjectClassName() + ":"
//                            + CacheWayEnum.MAGIC.getCode() + ":"
//                            + magicCacheParam.getFiledName() + ":"
//                            + magicCacheParam.getWhereType() + ":"
//                            + object.toString() + ":"
//                            + onlyCacheKeyTO.getKey();

                    //不需要业务唯一key，这样删除，不用*，不用模糊查找
                    key = cacheWrapper.getObjectClassName() + ":"
                            + CacheWayEnum.MAGIC.getCode() + ":"
                            + magicCacheParam.getFiledName() + ":"
                            + magicCacheParam.getWhereType() + ":"
                            + object.toString();
                    cacheKeyTOList.add(new CacheKeyTO(config.getNamespace(), key, onlyCacheKeyTO.getHfield()));
                }
            } else {
//                key = cacheWrapper.getObjectClassName() + ":"
//                        + CacheWayEnum.MAGIC.getCode() + ":"
//                        + magicCacheParam.getFiledName() + ":"
//                        + magicCacheParam.getWhereType() + ":"
//                        + paramObject.toString() + ":"
//                        + onlyCacheKeyTO.getKey();
                //不需要业务唯一key，这样删除，不用*，不用模糊查找
                key = cacheWrapper.getObjectClassName() + ":"
                        + CacheWayEnum.MAGIC.getCode() + ":"
                        + magicCacheParam.getFiledName() + ":"
                        + magicCacheParam.getWhereType() + ":"
                        + paramObject.toString();

                cacheKeyTOList.add(new CacheKeyTO(config.getNamespace(), key, onlyCacheKeyTO.getHfield()));
            }

        }
        return cacheKeyTOList;
    }
}
